/* ----------------------------------------------------------------------
 *	Shadow Quest
 *	Copyright (C) 2009 Prageeth Silva <prageeth@thenewcoders.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *  ----------------------------------------------------------------------
 */
package units;

import map.Item;
import map.World;

import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;

import panel.Panel;

import sq.FontLoader;
import sq.ImageLoader;
import sq.RPG;
import sq.game.Quest;

/** Represents the Player's character.
 * (Designed to be instantiated just once for the whole game).
 */
public class Player extends Fighter {

//	private static final float COLOR_CHANGE_PERCENTAGE = 0.6;
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    private static String PHOTO_NAME = "photo.png";
    // TODO private static String TALK_PHOTO = "talk.png";
    public static int DEFAULT_FRAME_WIDTH = 72, DEFAULT_FRAME_HEIGHT = 72;
    public static int STATUS_MAX = 5;
    public static int STATUS_DURATION = 3000;
    public static boolean SHOW_STATUS = true;
    private Panel panel;
    private Image photo;

    /** Create a new Player object
     * @param playerName The display name of the player.
     * @param maxHP The maximum HP of the player.
     * @param world The world in which the player exists.
     */
    public Player(World world, int team, String unitName)
            throws SlickException {
        super(world, team, unitName);

        Quest q = world.getQuest();
        this.setName(q.getPlayerName());

        q.setStatusDur(new int[STATUS_MAX]);
        for (int i = 0; i < STATUS_MAX; i++) {
            q.getStatusDur()[i] = 0;
        }

        this.setPanel(new Panel(world));

    }

    @Override
    protected void loadAllImages()
            throws SlickException {
        super.loadAllImages();
        this.setPhoto(ImageLoader.getLoadedImage(this.getDirectoryPath() + Player.PHOTO_NAME));
        // TODO add the TALK_PHOTO also
    }

    /** Update the player's state for a frame.
     * @param world A reference to the world.
     * @param input The input object, used to take the keyboard inputs.
     * @param delta Time passed since last frame (milliseconds).
     */
    @Override
    public void update(Input input, int delta) {

        // Update the player's movement direction based on keyboard presses.
        float dir_x = 0;
        float dir_y = 0;

        if (input.isKeyDown(Input.KEY_DOWN)) {
            dir_y += 1;
        }
        if (input.isKeyDown(Input.KEY_UP)) {
            dir_y -= 1;
        }
        if (input.isKeyDown(Input.KEY_LEFT)) {
            dir_x -= 1;
        }
        if (input.isKeyDown(Input.KEY_RIGHT)) {
            dir_x += 1;
        }

        boolean status_key = input.isKeyPressed(Input.KEY_S);
        boolean upgrade_key = input.isKeyPressed(Input.KEY_X);

        if (this.isAlive()) {

            if (upgrade_key) {
                this.setUsingUprageStatus(!this.isUsingUpgrade());
            }

            this.setCoolDown(this.getCoolDown() - delta);

            if (!this.isDieing()) {

                this.move(dir_x * delta * this.getSpeed(), dir_y * delta * this.getSpeed());

                for (Unit u : this.getWorld().getAllOtherUnits()) {

                    if (this.isEnemy(u)) {

                        double dx = (u.getX() - this.getX() < 0 ? u.getMaxX() : u.getMinX()) - this.getX();
                        double dy = (u.getY() - this.getY() < 0 ? u.getMaxY() : u.getMinY()) - this.getY();
                        double dist2 = calculateDistanceSqrd(dx, dy);

                        if (u.isAlive() && !u.isDieing() && isAttackAllowed(this, u)
                                && ((this.isRangedAttacker() && (dist2 <= Unit.Squared(PROJECTILE_RANGE))) || (dist2 <= Unit.Squared(CLOSE_PROXIMITY)))) {
                            this.attack(u, dx < 0 ? Direction.LEFT : Direction.RIGHT);
                            if (this.isRangedAttacker() && dist2 <= Unit.Squared(CLOSE_PROXIMITY)) {
                                this.setAutoWeaponChange(true);
                                this.setUsingUprageStatus(false);
                            }
                            break;
                        }

                    }

                }

                for (Item i : this.getWorld().getItems()) {
                    if (i.isOnMap() && !i.isInUse() && !i.isDestroyed() && (calculateDistanceSqrd(i.getX(), i.getY(), this.getX(), this.getY()) <= Unit.Squared(CLOSE_PROXIMITY))) {
                        this.pickUpItem(i);
                    }
                }

                if (!this.isAttacking()) {
                    this.setAttackingStatus(false);
                    if (!this.isMoving()) {
                        this.idle(delta);
                    }
                }

            } else {

                if (this.isRevivingAllowed() && this.isReviveAndTeleport())
                {
                    this.checkDieing(true, this.getReviveX(), this.getReviveY());
                }
                else
                {
                    this.checkDieing(false, 0, 0);
                }

            }

        } else {

            if (this.isTeleporting()) {
                this.teleport(this.getReviveX(), this.getReviveY());
            } else {
                this.revive();
            }

        }

        if (status_key) {
            this.setShowStatus(!this.isShowingStatus());
        }


        // TODO testing
        if (this.isPursuingTarget() && !this.isTargetReached()) {
            this.walkToTarget(delta);
        }


        // update the panel's inventory
        this.getPanel().update(this.getWorld(), input, delta);
        this.updateStatus(delta);

        super.update(input, delta);

    }

    /** Renders the player's status panel.
     * @param g The current Slick graphics context.
     * @throws SlickException 
     */
    public void renderStatus(Graphics g) {

        Quest q = this.getWorld().getQuest();

        // Display the status
        if (isShowingStatus()) {

            float BORDER = 10f, right = RPG.getScreenWidth() - BORDER, y = BORDER, x = right;
            int start = 0, end;
            g.setFont(FontLoader.getFontSize10());
            if (q.getStatus().size() > STATUS_MAX) {
                start = q.getStatus().size() - STATUS_MAX;
                end = start + STATUS_MAX;
            } else {
                end = q.getStatus().size();
            }
            int height = g.getFont().getLineHeight();
            if (q.getStatus().size() >= 0) {
                int c = 0;
                for (int i = start; i < end; i++) {
                    int time = q.getStatusDur()[c];
                    if (time > 0) {
                        g.setColor(new Color(1f, 1f, 1f, (float) time / STATUS_DURATION));
                        String text = q.getStatus().get(i);
                        float width = g.getFont().getWidth(text);
                        x = right - width;
                        g.drawString(text, x, y);
                        y += 2 * height;
                    }
                    c++;
                }
            }

        }

    }

    private boolean isShowingStatus() {
        return SHOW_STATUS;
    }

    private void setShowStatus(boolean showStatus) {
        SHOW_STATUS = showStatus;
    }

    private void updateStatus(int delta) {
        Quest q = this.getWorld().getQuest();
        for (int i = 0; i < STATUS_MAX; i++) {
            q.getStatusDur()[i] -= delta;
            if (q.getStatusDur()[i] < 0) {
                q.getStatusDur()[i] = 0;
            }
        }
    }

    public Panel getPanel() {
        return this.panel;
    }

    private void setPanel(Panel panel) {
        this.panel = panel;
    }

    /** Removes the an item from the inventory.
     * @param index The index of which the item to be removed is at.
     * @param x The x coordinate as to where the item should be dropped (in pixels).
     * @param y The y coordinate as to where the item should be dropped (in pixels).
     */
   // @Override
    public void dropInventoryItem(int index, float x, float y, boolean dropDuplicates) {
        super.dropInventoryItem(index, x, y, dropDuplicates);
        this.removeItemFromInventory();
    }

    /** Removes the an item from the inventory, but does not drop it on the map.
     * @param index The index of which the item to be removed is at.
     * @param unit The unit which the item is given to.
     */
    @Override
    public void giveItem(int index, Unit unit, boolean giveAll) {
        super.giveItem(index, unit, giveAll);
        this.removeItemFromInventory();
    }

    private void removeItemFromInventory() {
        int max = this.getInventory().size() / (Panel.INVENTORY_MAX_COLS * Panel.INVENTORY_MAX_ROWS + 1);
        if (this.getPanel().getInventoryGroup() > max) {
            this.getPanel().goUpGroup();
        }
    }

    public Image getPhoto() {
        return photo;
    }

    private void setPhoto(Image photo) {
        this.photo = photo;
    }
}
